/*
 * TcpConnection.cpp
 *
 *  Created on: Jul 26, 2016
 *      Author: Lucifer
 */

#include "./TcpClient.h"

namespace BFX {

namespace IMPL {
/// Connect/Read/Write channel uses for BFX::TcpClient
class TcpClientChannel : public SelectableChannel {
public:
	TcpClientChannel(Socket* socket, TcpClient* client) :
		SelectableChannel(socket), _client(client) {
		BFX_ASSERT(client);
	}
	virtual ~TcpClientChannel() {
		close();
	}

	/// @copydoc SelectableChannel::getValidOps()
	virtual int getValidOps() const {
		return SelectionKey::OP_READ | SelectionKey::OP_WRITE | SelectionKey::OP_CONNECT;
	}
	/// @copydoc SelectableChannel::setReadyOps()
	virtual bool setReadyOps(int ops, SelectionKey* key) {
		BFX_ASSERT(key);
		int oldOps = key->getReadyOps();
		int newOps = (ops & key->getInterestOps());

		key->setReadyOps(newOps);
		return !!(newOps & ~oldOps);
	}

	TcpClient* _client;
};
}	// namespace IMPL

TcpClient::TcpClient() :
		_active(false), _connected(false), _callback(nullptr) {
	_curReadyOps = 0;
	_channel = new IMPL::TcpClientChannel(new Socket(), this);
}
TcpClient::TcpClient(Socket* socket) :
		_active(false), _connected(true), _callback(nullptr) {
	BFX_ASSERT(socket);
	_curReadyOps = 0;
	_channel = new IMPL::TcpClientChannel(socket, this);
}
TcpClient::~TcpClient() {
	stop();
}
bool TcpClient::connect(InetAddress* addr) {
	_connected = _channel->getSocket()->connect(addr);
	return _connected;
}
bool TcpClient::start(Selector* sel, ClientCallback callback) {
	//already listening
	if (_active) {
		return false;	// already started.
	}
	_active = true;
	_callback = callback;
	int ops = _connected ? (SelectionKey::OP_READ | SelectionKey::OP_WRITE) :
					SelectionKey::OP_CONNECT;
	_channel->registerKey(sel, ops, eventCallback);
	return _active;
}

void TcpClient::stop() {
	if (!_active)
		return;
	_active = false;
	_channel->close();
}
void TcpClient::eventCallback(SelectionKey* key) {
	BFX_ASSERT(key);
	IMPL::TcpClientChannel* channel = reinterpret_cast<IMPL::TcpClientChannel*>(key->getChannel());

	// TODO except events

	// Notify the caller with the source client.
	TcpClient* client = channel->_client;
	BFX_ASSERT(client && client->_active);
	TcpClient::ClientCallback callback = client->_callback;

	client->_curReadyOps = key->getReadyOps();
	callback(client);
	client->_curReadyOps = 0;

	// _connected will be set in TcpClient::connect().
	if (key->isConnectable() && client->_connected) {
		// connect successful, now we need to reset interest ops.
		key->setInterestOps(SelectionKey::OP_READ | SelectionKey::OP_WRITE, eventCallback);
	}
}

} /* namespace BFX */
